﻿/// <reference path="zepto.js />

/*jslint browser: true, confusion: true, undef: true, sloppy: true, vars: true, white: true, nomen: true, plusplus: true, maxerr: 50, indent: 4 */

var GT = (function () {

    /**********************************************************************************************
    LOCAL VARIABLES
    **********************************************************************************************/

    var _me,
        _currentPage;


    /**********************************************************************************************
    CONSTANTS
    **********************************************************************************************/

    var CLASS_DISABLED = "disabled";
    var CLASS_IS_BUTTON = "is-button";

    var MAXIMUM_MORE_ITEMS = 40; // Maximum number of items to display when clicking a more button.
    var MORE_ITEMS_IN_PAGE = 10; // How many items to retrieve when clicking a more button.
    var TYPE_SINGLE = "type-single"; // More button can only operate once.
    var TYPE_MULTIPLE = "type-multiple"; // More button operates multiple times.
	
	var HAS_TAP = (typeof ($.os.version) !== "undefined");
	//var EVENT_CLICK = HAS_TAP ? "tap" : "click";
	var EVENT_CLICK = "click";

    /**********************************************************************************************
    PAGES INITIALIZATION MECHANISM
    **********************************************************************************************/

    var _pages = [
            {
                name: "home",
                init: null
            },

            {
                name: "news",
                init: null
            },

            {
                name: "tv",
                init: null
            },

            {
                name: "channels",
                init: null
            },

			{
			    name: "item",
			    init: null
			},

            {
                name: "appstore",
                init: null
            },

            {
                name: "appstore-item",
                init: function () {
                    initCarousel("#images .carousel");
                }
            },

			{
			    name: "appstore-category",
			    init: null
			},

			{
			    name: "pricelist",
			    init: null
			}
        ];

    function init() {
        _me = this;

        // Prevent FOUC on breadcrumbs
        window.onload = function () {
            $(".bread-crumbs li").css("visibility", "visible");
        };

        // Common init
        /*initTapIntent();*/

        // Init "more" buttons if needed		
        var $footerDown = $(".footer-down");
        if ($footerDown.length > 0) {
            var buttonType = $footerDown.hasClass(TYPE_MULTIPLE) ? TYPE_MULTIPLE : TYPE_SINGLE;
            initMoreButtons(".footer-down .button-more", ".more", buttonType);
        }

        // Which is the current page?
        _currentPage = getCurrentPage();

        // Initialize it (if there's anything to initialize).
        if (typeof _currentPage["init"] === "function") {
            _currentPage.init();
        }
    }

    function getCurrentPage() {
        var $body = $("body");
        var currentPage;

        for (i = 0; i < _pages.length; i++) {
            if ($body.hasClass("page-" + _pages[i].name)) {
                currentPage = _pages[i];
                break;
            }
        }

        return currentPage;
    }


    /**********************************************************************************************
    GENERAL FUNCTIONALITIES
    **********************************************************************************************/	

    /*	a method to follow links when the "tap" event fires and disable the follow on click */
    function initTapIntent() {
        if (typeof ($.os.version) == "undefined") {
            return;
        }

        /*	set one handler to prevent all click events */
        $("html").get(0).addEventListener("click", function (evt) {
            evt.preventDefault();
						
        }, true);

        /*	set one handler to catch all tap events and follow the url if it's a link */
        $("html").get(0).addEventListener("tap", function (evt) {
            $target = $(evt.target);

            if ($target.is("a") === false) {
                $target = $target.closest("a");
            }

            if ($target.is("a") &&
				$target.hasClass(CLASS_IS_BUTTON) === false) {
                window.location = $target.get(0).href;
				evt.stopPropagation();
            }			

        }, true);
    }

    function initCarousel(container, animate) {
        var $container = $(container),
            $arrowNext = $container.find(".arrow-next").addClass(CLASS_IS_BUTTON),
            $arrowPrev = $container.find(".arrow-prev").addClass(CLASS_IS_BUTTON),
            $frame = $container.find(".frame"),
            $list = $container.find(".frame > UL");        

        // Calculate the number of items in this carousel.
        var totalItems = $list.find("LI").get().length;

        if (totalItems == 0) {            
			updateArrowsStates();			
			return;
        }

        // Get the width of each item (which is actually the step size).
        var itemWidth = $list.find("LI").first().width();
        var listWidth = $list.width();

        // Holds the current position in the carousel.
        var currentPosition = 0;

        // Handle move-next animation.
        function nextHandler(evt) {
            evt.preventDefault();
			evt.stopPropagation();
            currentPosition++;
            if (animate) {
                $list.css("-webkit-transform", "translateX(" + (itemWidth * currentPosition) + "px)");
            } else {
                $list.css("right", -(itemWidth * currentPosition) + "px");
            }
            updateArrowsStates();
        }

        // Handle move-prev animation.
        function prevHandler(evt) {
            evt.preventDefault();
            evt.stopPropagation();
			currentPosition--;
            if (animate) {
                $list.css("-webkit-transform", "translateX(" + (itemWidth * currentPosition) + "px)");
            } else {
                $list.css("right", -(itemWidth * currentPosition) + "px");
            }
            updateArrowsStates();
        }
        //$frame.swipe(function(a,b,c){ for (var key in a) { alert([key, a[key]]); } });
        $frame.swipe(nextHandler);

        // According to the current position of the carousel and the total number of items, this
        // function determines whether each button is clickable or not.
        function updateArrowsStates() {
            // Has next?
            if (currentPosition < totalItems - 1) {
                $arrowNext.removeClass(CLASS_DISABLED).unbind(EVENT_CLICK).bind(EVENT_CLICK, nextHandler);
            } else {
                $arrowNext.addClass(CLASS_DISABLED).unbind(EVENT_CLICK);
            }

            // Has prev?
            if (currentPosition > 0) {
                $arrowPrev.removeClass(CLASS_DISABLED).unbind(EVENT_CLICK).bind(EVENT_CLICK, prevHandler);
            } else {
                $arrowPrev.addClass(CLASS_DISABLED).unbind(EVENT_CLICK);
            }
        }

        updateArrowsStates();
    }


    /**********************************************************************************************
    BUTTON ANIMATION
    **********************************************************************************************/

    /*	General controller for animation toggled by buttons.
    Receives a button instance and sets it up to animate it's defined container
    upon firing the click/tap event on the button. */
    function initAnimButton(button) {       

        /*	Perform logic before the event is bound */
        button.init();

        $(button.selector).bind(EVENT_CLICK, function (evt) {
            /*	Prevent default actions such as going to a link */
            
			evt.preventDefault();
			evt.stopPropagation();
            /*	Execute default actions for the event, BEFORE open/close logic */
            button.start();

            /*	Execute actions based on the state of the button */
            if (button.isOpen) {
                button.close();
            } else {
                button.open();
            }

            /*	Execute default actions for the event, AFTER open/close logic */
            button.end();			
        });
    }

    /*	Setup the Categories-Button to open/close the categories upon click/tap */
    function initCategoriesButton() {
        var cb = new CategoriesButton(".button-all-categories", "#header");
        initAnimButton(cb);
    }

    /*	Setup the More-Buttons to open/close additional items upon click/tap */
    function initMoreButtons(buttons, hidden, type) {
        $(buttons).each(function (index, button) {
            var mb = new MoreButton(button, hidden, type);
            initAnimButton(mb);
        });
    }


    /**********************************************************************************************
    CLASS BUTTON
    The base class for buttons which open/close containers
    **********************************************************************************************/

    function Button(selector, containerSelector, isOpen, closeText) {
        this.selector = selector;
        this.containerSelector = containerSelector;

        /*	Tells us the state of the container */
        this.isOpen = isOpen || false;

        /*	Text to display when the container is opened */
        this.closeText = closeText || "סגור";

        /*	A class that will be added to the container when it is opened */
        this.openClass = "slideDown";

        /*	Cache references to Zepto.js objects */
        this.$button = $(selector);
        this.$container = $(containerSelector);

        /*	The original text of the button that will be restored when
        the container is closed after it's opened. */
        this.originalText = this.$button.html();
    }

    Button.prototype.init = function () {
        /*	Add a special class to all of the buttons, so the global click/tap handler
        would know that a button was clicked/tapped and not a regular element. */
        this.$button.addClass(CLASS_IS_BUTTON);
    };

    /*	Define base methods */
    Button.prototype.start = function () { };
    Button.prototype.end = function () { };
    Button.prototype.open = function () { };
    Button.prototype.close = function () { };


    /**********************************************************************************************
    CLASS CATEGORIES-BUTTON
    Defines the behaviour of the button which is used to open/close the categories
    **********************************************************************************************/

    CategoriesButton.prototype = new Button();

    function CategoriesButton(selector, containerSelector, isOpen, closeText) {
        this.constructor.apply(this, arguments); //call base c'tor
    }

    CategoriesButton.prototype.open = function () {
        this.$container.addClass(this.openClass);
        this.$button.html(this.closeText);
        this.isOpen = true;
    };

    CategoriesButton.prototype.close = function () {
        this.$container.removeClass(this.openClass);
        this.$button.html(this.originalText);
        this.isOpen = false;
    };


    /**********************************************************************************************
    CLASS MORE-BUTTON
    Defines the behaviour of the buttons which are used to open/close more items
    of their defined container.
    **********************************************************************************************/

    MoreButton.prototype = new Button();

    function MoreButton(selector, hiddenSelector, type) {
        this.constructor.call(this, selector, selector);

        /*	Define new properties */
        this.$boxList = this.$button.closest(".box-list");
        this.$text = this.$button.find(".more-text");
        this.$play = this.$button.find(".play");
        this.originalHeight = 0;
        this.hiddenSelector = hiddenSelector;
        this.type = type;

        /*	Override Button's */
        this.$container = this.$boxList.find("ul");

        /*  Handle retrieving more items multiple times. */
        if (this.type === TYPE_MULTIPLE) {
            var totalResults = parseInt(this.$boxList.find(".totalResults").val());
            this.totalResults = Math.min(totalResults, MAXIMUM_MORE_ITEMS);
            this.pageSize = MORE_ITEMS_IN_PAGE;
            this.currentPage = 1; // First request must always be for page no. 1.
            this.initialResults = this.$container.find("LI").length;
        }
    }


    /**********************************************************************************************
    CLASS MORE-BUTTON: OVERRIDE PARENT METHODS
    **********************************************************************************************/

    MoreButton.prototype.open = function () {
        var self = this;
        var url = this.$button.attr("href");

        if (this.type === TYPE_MULTIPLE) {
            url += "&page=" + this.currentPage + "&pageSize=" + this.pageSize;
        }

        /*	AJAX CALL */
        $.get(
			url,

			function (data) {
			    /*	Append the new items to container
					First "quarantine" them: append hidden to the DOM, add a CSS Class to mark them as new items
					and then append to the right place and remove the "quarantine" */
				self.$container.append("<li id='quarantine' style='display:none;'><ul></ul></li>");
				var $quarantine = self.$container.find("#quarantine > UL");
				$quarantine.append(data);
				$quarantine.find("LI").each(function(index, element) {
					$(element).addClass("more");
					self.$container.append(element);
				});
				$quarantine.remove();

			    /*	Animate to new height */
			    var moreHeight = self.findNewHeight();
			    var currentHeight = self.$container.height();
			    self.animateOpen(currentHeight, moreHeight);

			    if (self.type === TYPE_SINGLE) {
			        /*  Change button state */
			        self.changeState(true, currentHeight);
			    }
			    else if (self.type === TYPE_MULTIPLE) {
			        /*  Increment page counter */
			        self.currentPage++;

			        /*  Calculate if reached total results */
			        var reachedTotal = (self.currentPage - 1) * self.pageSize + self.initialResults >= self.totalResults;
			        if (reachedTotal) {
			            // hide 'more' button.
			            self.$boxList.find(".footer").hide();
					}
			    }
			}
		);
    };

    MoreButton.prototype.close = function () {
        if (this.type === TYPE_SINGLE) {
            /*	Remove items and class */
            this.$container.removeClass(this.openClass);
            this.$container.find(this.hiddenSelector).remove();

            /*	Animate to previous height */
            this.$container.css("height", this.originalHeight + "px");

            /*	Clean-up old animation properties for the next one */
            var self = this;
            setTimeout(function () {
                self.$container.attr("style", "");
            }, 1000);

            /*	Change button state*/
            this.changeState(false);
        }
    };


    /**********************************************************************************************
    CLASS MORE-BUTTON: DEFINE OWN HELPER METHODS
    **********************************************************************************************/

    /*	Animate the container to show more items */
    MoreButton.prototype.animateOpen = function (currentHeight, moreHeight) {
        var self = this;

        /*	MUST preserve this sequence/delays:
        1. Explicitly set the container's height
        2. After some delay set the transition 
        3. After some delay set the NEW height and start the transition
        4. After TRANSITION DURATION add a class to state container is opened */
        this.$container.css("height", currentHeight + "px");
        setTimeout(function () {
            self.$container.css({ "-webkit-transition": "all 1s ease" });
        }, 1);

        setTimeout(function () {
            self.$container.css("height", (currentHeight + moreHeight) + "px");
        }, 2);

        setTimeout(function () {
            self.$container.addClass(self.openClass);
        }, 500);

        // Cleanup when the animation ended, and prepate for next time!
        if (this.type == TYPE_MULTIPLE) {
            setTimeout(function () {
                self.$container.find("LI.more").removeClass("more");
                self.$container.removeClass(self.openClass);
                self.$container.attr("style", "");
            }, 500);
        }
    };

    /*	Change the state of the button when opening/closing it's container */
    MoreButton.prototype.changeState = function (isOpen, currentHeight) {
        if (this.type == TYPE_MULTIPLE) {
			return;
		}

        if (isOpen) {
            this.isOpen = true;
            this.originalText = this.$text.html();
            this.originalHeight = currentHeight;
            this.$text.html(this.closeText);
            this.$play.css("-webkit-transform", "rotate(180deg)");
        } else {
            this.isOpen = false;
            this.$text.html(this.originalText);
            this.$play.css("-webkit-transform", "rotate(0deg)");
        }
    };

    /*	Find new height by cloning and getting the height delta of new items */
    MoreButton.prototype.findNewHeight = function () {
        /*	Clone the container and append it after the real container */
        var $clone = $(this.$container.get(0).cloneNode(true));
        this.$container.after($clone.get(0));

        /*	Visually hide the clone */
        $clone.css({
            position: "absolute",
            visibility: "hidden"
        });

        /*	Expand the additional items */
        $clone.find(this.hiddenSelector).css({ height: "auto" });

        /*	Calc the new height */
        var newHeight = $clone.height() - this.$container.height();

        /*	Remove the clone */
        $clone.remove();
        return newHeight;
    };


    /**********************************************************************************************
    PUBLIC METHODS
    **********************************************************************************************/

    return {
        "init": init
    };
}()).init();